package cn.itcast.nems.studentrecord.service.impl;

import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.exception.v2.BizException;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.ic.common.exception.v2.bean.BizStatusEnum;
import cn.itcast.nems.order.api.OrderApi;
import cn.itcast.nems.order.dto.OrderModeLineDTO;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dto.change.*;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordReceivableCreateDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordReferenceDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordTransactionCreateDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordValidMessageDTO;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordSourceEnum;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordStateEnum;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordTransactionStateEnum;
import cn.itcast.nems.studentrecord.service.settlement.SettlementResult;
import cn.itcast.nems.studentrecord.service.studentrecord.*;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Consumer;

/**
 * @author liyong
 * create: 2023-11-21 15:56:22
 **/
@AllArgsConstructor
public abstract class StudentRecordChangeServiceImpl implements StudentRecordChangeService {
    final Logger logger = LoggerFactory.getLogger(this.getClass());

    final StudentRecordService studentRecordService;
    final StudentRecordCreateService studentRecordCreateService;
    final StudentRecordTransactionService studentRecordTransactionService;
    final StudentRecordMessageService studentRecordMessageService;
    final StudentRecordReceivableService studentRecordReceivableService;
    final OrderApi orderApi;

    /**
     * 添加学籍应收明细
     *
     * @param createDTO 参数对象
     */
    void addStudentRecordReceivable(StudentRecordReceivableCreateDTO createDTO) {
        this.studentRecordReceivableService.addStudentRecordReceivable(createDTO);
    }

    /**
     * 检查学籍变更是否允许
     *
     * @param studentRecordChangeDTO 学籍变更信息
     * @param checkerPostProcessor   检测器处理完成后的回调处理器
     * @throws BizException 不允许时抛异常
     */
    void checkChange(StudentRecordChangeDTO studentRecordChangeDTO,
                     Consumer<StudentRecordChangeChecker> checkerPostProcessor) {
        StudentRecordChangeCheckDTO dto = new StudentRecordChangeCheckDTO();
        dto.setStudentId(studentRecordChangeDTO.getStudentId());
        dto.setInDate(studentRecordChangeDTO.getTargetClazzInDate());
        dto.setOutDate(studentRecordChangeDTO.getOriginClazzOutDate());
        dto.setTargetClazzId(studentRecordChangeDTO.getTargetClazzId());
        String originStudentRecordId = studentRecordChangeDTO.getOriginStudentRecordId();
        final StudentRecordChangeCheckResult result = this.doCheck(originStudentRecordId, dto, checkerPostProcessor);
        if (!result.canChange()) {
            logger.warn("学籍变更失败 {} {}", result.reason(), studentRecordChangeDTO);
            BizExceptionProducer.throwBadRequest(BizStatusEnum.BAD_REQUEST.name(), result.reason());
        }
    }

    @Override
    public StudentRecordChangeCheckResult canChange(String originStudentRecordId, StudentRecordChangeCheckDTO dto) {
        return this.doCheck(originStudentRecordId, dto, null);
    }

    StudentRecordChangeCheckResult doCheck(String originStudentRecordId,
                                           StudentRecordChangeCheckDTO dto,
                                           Consumer<StudentRecordChangeChecker> checkerPostProcessor) {
        final StudentRecord st = this.studentRecordService.getById(originStudentRecordId);
        Assert.notNull(st, BizExceptionProducer.serverError(BizStatusEnum.BAD_REQUEST.toString(), "学籍ID '%s' 不存在", originStudentRecordId));
        StudentRecordChangeCheckOriginDTO originDTO = new StudentRecordChangeCheckOriginDTO();
        BeanUtils.copyProperties(st, originDTO);
        List<StudentRecordTransactionChangeCheckDTO> transactions = this.studentRecordTransactionService.findStudentRecordTransactionChangeChecks(dto.getStudentId());
        StudentRecordChangeChecker checker = this.createStudentRecordChangeChecker(originDTO, transactions);
        if (checkerPostProcessor != null) {
            checkerPostProcessor.accept(checker);
        }
        return checker.canChange(dto);
    }

    StudentRecordChangeChecker createStudentRecordChangeChecker(StudentRecordChangeCheckOriginDTO studentRecordOrigin,
                                                                List<StudentRecordTransactionChangeCheckDTO> transactions) {
        return new StudentRecordChangeChecker(studentRecordOrigin, transactions);
    }

    /**
     * 学籍变更完成（学籍变成有效）
     *
     * @param studentRecord 学籍ID
     * @param inDate        入班日期
     */
    void studentRecordChangeComplete(StudentRecord studentRecord, LocalDate inDate) {
        if (StudentRecordStateEnum.VALID.name().equals(studentRecord.getState())
                && studentRecord.getLockFlag() != null && studentRecord.getLockFlag()) {
            logger.warn("学籍'{}'已经是有效状态并且已解锁", studentRecord.getId());
            return;
        }
        StudentRecord update = new StudentRecord();
        update.setId(studentRecord.getId());
        // update.setLockFlag(false);
        if (inDate != null) {
            update.setInDate(inDate.atStartOfDay());
        }
        update.setState(StudentRecordStateEnum.VALID.name());
        this.studentRecordService.updateStudentRecordById(update);

        StudentRecordValidMessageDTO message = new StudentRecordValidMessageDTO();
        message.setStudentRecordId(studentRecord.getId());
        message.setBizDateTime(LocalDateTime.now());
        message.setTenantId(studentRecord.getTenantId());
        message.setSubtenantId(studentRecord.getSubtenantId());
        message.setClazzId(studentRecord.getClazzId());
        this.studentRecordMessageService.studentRecordValid(message);

        this.studentRecordService.unlock(studentRecord.getId());
    }

    /**
     * 创建目标班级学籍
     *
     * @param changeDTO   学籍变更参数
     * @param orderId     订单ID，如果没有订单未null
     * @param clearInDate 是否要清除学籍上的入班日期
     * @return 新创建的目标变学籍
     * @throws BizException 如果学籍已经存在
     */
    StudentRecord createTargetStudentRecord(StudentRecordChangeDTO changeDTO, String orderId, boolean clearInDate) {
        final StudentRecordSourceEnum s = StudentRecordSourceEnum.valueOf(changeDTO.getJournalType().name());
        StudentRecordCreateDTO dto = changeDTO.createTargetStudentRecordDTO(s);
        dto.setOrderId(orderId);
        if (clearInDate) {
            dto.setInDate(null);
        }
        return this.studentRecordCreateService.createStudentRecord(dto);
    }

    /**
     * 创建学籍流转记录
     *
     * @param dto              创建学籍参数
     * @param settlementResult 结算数据，没有结算可以为null
     * @param type             出入班类型， 见： StudentRecordTransaction.TYPE_OUT StudentRecordTransaction.TYPE_IN
     */
    void createStudentRecordTransaction(StudentRecordReferenceDTO dto,
                                        SettlementResult settlementResult,
                                        int type) {
        StudentRecordTransactionCreateDTO createDTO = new StudentRecordTransactionCreateDTO();
        createDTO.setStudentRecordId(dto.getStudentRecordId());
        createDTO.setReferenceType(dto.getReferenceType());
        createDTO.setReferenceId(dto.getReferenceId());
        createDTO.setReferenceLineId(dto.getReferenceLineId());
        createDTO.setClazzId(dto.getClazzId());
        createDTO.setClassName(dto.getClazzName());
        createDTO.setPhaseNumber(1);
        createDTO.setActualAmountDue(dto.getActualAmountDue());
        createDTO.setType(type);
        createDTO.setBizDate(dto.getBizDate());
        createDTO.setState(StudentRecordTransactionStateEnum.VALID);

        if (settlementResult != null) {
            createDTO.setServiceDays(settlementResult.getServiceDays());
            createDTO.setSettlementDays(settlementResult.getSettlementDays());
            createDTO.setSettlementAmount(settlementResult.getSettlementAmount());
            createDTO.setActualAmountDue(settlementResult.getSettlementAmount());
        }
        this.studentRecordTransactionService.createStudentRecordTransaction(createDTO);
    }

    /**
     * 填充凭单中的订单信息
     *
     * @param studentRecordId      学籍ID
     * @param studentRecordJournal 凭单信息
     */
    void popOrderIfExsit(String studentRecordId, StudentRecordJournalDTO studentRecordJournal) {
        String orderId = studentRecordJournal.getOrderId();
        if (!StringUtils.hasText(orderId)) {
            orderId = this.studentRecordService.findOrderId(studentRecordId);
            if (StringUtils.hasText(orderId)) {
                logger.info("转入班'{}'对应的订单'{}'", studentRecordId, orderId);
                final List<OrderModeLineDTO> orderLines = this.orderApi.findOrderLines(orderId);
                final String orderLineId = orderLines.get(0).getId();
                studentRecordJournal.setOrderId(orderId);
                studentRecordJournal.setOrderLineId(orderLineId);
            }
        }
    }
}
